package cn.b.sky.shop.activiti.cmd;

import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.engine.ActivitiException;
import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.activiti.engine.impl.interceptor.Command;
import org.activiti.engine.impl.interceptor.CommandContext;
import org.activiti.engine.impl.util.io.StreamSource;
import org.activiti.validation.ProcessValidator;
import org.activiti.validation.ValidationError;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;

public class BpmnParseCmd implements Command<BpmnModel> {

  private static final Logger LOGGER = LoggerFactory.getLogger(BpmnParseCmd.class);

  protected boolean validateSchema = true;

  protected boolean validateProcess = true;

  protected BpmnModel bpmnModel;

  protected StreamSource streamSource;

  public BpmnParseCmd(StreamSource streamSource, boolean validateSchema, boolean validateProcess) {
    this.streamSource = streamSource;
    this.validateSchema = validateSchema;
    this.validateProcess = validateProcess;
  }

  @Override
  public BpmnModel execute(CommandContext commandContext) {
    try {

      ProcessEngineConfigurationImpl processEngineConfiguration = commandContext.getProcessEngineConfiguration();
      BpmnXMLConverter converter = new BpmnXMLConverter();

      boolean enableSafeBpmnXml = false;
      String encoding = null;
      if (processEngineConfiguration != null) {
        enableSafeBpmnXml = processEngineConfiguration.isEnableSafeBpmnXml();
        encoding = processEngineConfiguration.getXmlEncoding();
      }

      if (encoding != null) {
        bpmnModel = converter.convertToBpmnModel(streamSource, validateSchema, enableSafeBpmnXml, encoding);
      } else {
        bpmnModel = converter.convertToBpmnModel(streamSource, validateSchema, enableSafeBpmnXml);
      }

      // XSD validation goes first, then process/semantic validation
      if (validateProcess) {
        ProcessValidator processValidator = processEngineConfiguration.getProcessValidator();
        if (processValidator == null) {
          LOGGER.warn(
              "Process should be validated, but no process validator is configured on the process engine configuration!");
        } else {
          List<ValidationError> validationErrors = processValidator.validate(bpmnModel);
          if (validationErrors != null && validationErrors.size() > 0) {

            StringBuilder warningBuilder = new StringBuilder();
            StringBuilder errorBuilder = new StringBuilder();

            for (ValidationError error : validationErrors) {
              if (error.isWarning()) {
                warningBuilder.append(error.toString());
                warningBuilder.append("\n");
              } else {
                errorBuilder.append(error.toString());
                errorBuilder.append("\n");
              }
            }

            // Throw exception if there is any error
            if (errorBuilder.length() > 0) {
              throw new ActivitiException("Errors while parsing:\n" + errorBuilder.toString());
            }

            // Write out warnings (if any)
            if (warningBuilder.length() > 0) {
              LOGGER.warn("Following warnings encountered during process validation: " + warningBuilder.toString());
            }

          }
        }
      }
    } catch (ActivitiException e) {
      throw e;
    } catch (Exception e) {
      throw new ActivitiException("Error parsing XML", e);
    }

    return this.bpmnModel;
  }

}
